//-----------------------------------------------------------------------------
//
//  <No description>
//
//  Microsoft Zenith Project
//  Copyright ©2001,2002,2003,2004 Microsoft Corporation
//  One Microsoft Way, Redmond, Washington 98052-6399 U.S.A.
//  All rights reserved.
//  MICROSOFT CONFIDENTIAL
//
//-----------------------------------------------------------------------------

#include <tinyhal.h>
#include "AT91_GPIO.h"

//--//

#undef  TRACE_ALWAYS

#define TRACE_ALWAYS               0x00000001

#undef  DEBUG_TRACE
#define DEBUG_TRACE (TRACE_ALWAYS)

#define DEBUG	0
#if DEBUG
#define DBG				hal_printf
#define DBGGPIO(f,x)	hal_printf(f ": " #x "=%d\r\n",x)
#else
#define DBGGPIO(f,x)
#define DBG				nul
static void nul(char *fmt, ...)
{
}
#endif

//--//

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section rodata = "c_Gpio_Attributes_AT91"
#endif

const UINT8 AT91_GPIO_Driver::c_Gpio_Attributes[AT91_GPIO_Driver::c_MaxPins] =
{
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   0   (register 0)
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   1
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   2
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   3
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   4
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   5
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   6
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   7
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   8
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //   9
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  10
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  11
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  12
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  13
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  14
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  15
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  16
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  17
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  18
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  19
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  20
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  21
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  22
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  23
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  24
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  25
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  26
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  27
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  28
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  29
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  30
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  31
#if (AT91_MAX_GPIO > 32)
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  32   (register 1)
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  33
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  34
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  35
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  36
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  37
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  38
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  39
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  40
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  41
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  42
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  43
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  44
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  45
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  46
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  47
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  48
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  49
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  50
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  51
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  52
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  53
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  54
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  55
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  56
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  57
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  58
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  59
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  60
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  61
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  62
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  63
#endif
#if (AT91_MAX_GPIO > 64)
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  64   (register 2)
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  65
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  66
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  67
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  68
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  69
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  70
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  71
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  72
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  73
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  74
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  75
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  76
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  77
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  78
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  79
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  80
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  81
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  82
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  83
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  84
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  85
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  86
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  87
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  88
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  89
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  90
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  91
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  92
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  93
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  94
    GPIO_ATTRIBUTE_INPUT | GPIO_ATTRIBUTE_OUTPUT, //  95
#endif
};

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section rodata
#endif

//--//

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section zidata = "g_AT91_GPIO_Driver"
#endif

AT91_GPIO_Driver g_AT91_GPIO_Driver;

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section zidata
#endif

//--//

BOOL AT91_GPIO_Driver::Initialize()
{
    int i;
    struct AT91_PIO * pPioX;

    // initialize the interrupt information
    {
        PIN_ISR_DESCRIPTOR* pinIsr = g_AT91_GPIO_Driver.m_PinIsr;

        for(i = 0; i < c_MaxPins; i++)
        {
            pinIsr->m_pin     = i;
            pinIsr->m_intEdge = GPIO_INT_NONE;
            pinIsr->m_isr     = STUB_GPIOISRVector;
            pinIsr->m_param   = NULL;

            pinIsr->m_completion.Initialize( );
            pinIsr->m_completion.InitializeForISR( &PIN_ISR_DESCRIPTOR::Fire, pinIsr );

            pinIsr++;
        }
    }

    SetDebounce( 20 );

    for(i = 0; i < c_MaxPorts; i++)
    {
        // initialize pins as free
        g_AT91_GPIO_Driver.m_PinReservationInfo[i] = 0;
		pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + (i * 0x200));

        pPioX->PIO_IDR = 0xffffffff;                // Disable all interrupts
        pPioX->PIO_ISR ^= 0xffffffff;
    }

    // set unsed pins as specified in platform selector
#if defined(AT91_UNUSED_GPIOS)
    {
        struct UnusedGPIO
        {
            UINT8 Pin;
            UINT8 State;
        };

        static const UnusedGPIO c_Unused[] = { AT91_UNUSED_GPIOS };

        const UnusedGPIO* ptr = c_Unused;

        for(size_t i = 0; i < ARRAYSIZE(c_Unused); i++, ptr++)
        {
            if(ptr->State == RESISTOR_DISABLED)
            {
                EnableInputPin( (GPIO_PIN)ptr->Pin, FALSE, NULL, 0, GPIO_INT_NONE, RESISTOR_DISABLED );
            }
            else
            {
                EnableOutputPin( (GPIO_PIN)ptr->Pin, (BOOL)(ptr->State == RESISTOR_PULLUP) );
            }
        }
    }
#endif

    // register interrupt handler for all ports
    if(!CPU_INTC_ActivateInterrupt( AT91C_ID_PIOA, ISR, (void*)(size_t)0 )) return FALSE;
    if(!CPU_INTC_ActivateInterrupt( AT91C_ID_PIOB, ISR, (void*)(size_t)1 )) return FALSE;
#if (AT91_MAX_GPIO > 64)
    if(!CPU_INTC_ActivateInterrupt( AT91C_ID_PIOC, ISR, (void*)(size_t)2 )) return FALSE;
#endif

    // TODO: will be replaced by PMC API
    AT91_PMC &pmc = AT91::PMC();
    pmc.EnablePeriphClock(AT91C_ID_PIOA);
    pmc.EnablePeriphClock(AT91C_ID_PIOB);
#if  (AT91_MAX_GPIO > 64)
    pmc.EnablePeriphClock(AT91C_ID_PIOC);
#endif

    return TRUE;
}

//--//

BOOL AT91_GPIO_Driver::Uninitialize()
{
	int i;
	UINT32 volatile trash;
	struct AT91_PIO * pPioX = (struct AT91_PIO *)AT91C_BASE_PIOA;
	
    GLOBAL_LOCK(irq);

    for(i = 0; i < c_MaxPorts; i++)
    {
		pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + (i * 0x200));
        pPioX->PIO_IDR = 0xffffffff;            // Disable all interrupts
        trash = pPioX->PIO_ISR;					// flush pending
    }

    return TRUE;
}

//--//

UINT32 AT91_GPIO_Driver::Attributes( GPIO_PIN Pin )
{
	DBGGPIO("Attributes",Pin);

	if(Pin < c_MaxPins)
    {
        return c_Gpio_Attributes[Pin];
    }
    return GPIO_ATTRIBUTE_NONE;
}

//--//

void AT91_GPIO_Driver::DisablePin( GPIO_PIN pin, GPIO_RESISTOR resistorState, UINT32 dir_out, GPIO_ALT_MODE function )
{
    struct AT91_PIO * pPioX;

	DBGGPIO("DisablePin",pin);

    ASSERT(pin < c_MaxPins);

    UINT32  bitmask = 1 << PinToBit ( pin );
    UINT32  port = PinToPort( pin );

	pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + port * 0x200);

    GLOBAL_LOCK(irq);

    if(resistorState == RESISTOR_DISABLED)
        pPioX->PIO_PPUDR = bitmask;         // Disable the pull up resistor
    else if(resistorState == RESISTOR_PULLUP)
        pPioX->PIO_PPUER =  bitmask;            // Enable the pull up resistor

	switch (function)
    {
        case GPIO_ALT_MODE_1:               // Enable Peripheral A function
            pPioX->PIO_PDR = bitmask;
            pPioX->PIO_ASR = bitmask;
            break;


        case GPIO_ALT_MODE_2:               // Enable Peripheral B function
            pPioX->PIO_PDR = bitmask;
            pPioX->PIO_BSR = bitmask;
            break;

        default:
            break;
    }

	// fix: 110608, VG -- set output/input resp to dir_out
	if (dir_out)
		pPioX->PIO_OER = bitmask;
	else
		pPioX->PIO_ODR = bitmask;

    pPioX->PIO_IDR = bitmask;                   // Disable the Input Change Interrupt

    PIN_ISR_DESCRIPTOR& pinIsr = g_AT91_GPIO_Driver.m_PinIsr[ pin ];

    pinIsr.m_intEdge = GPIO_INT_NONE;
    pinIsr.m_isr     = STUB_GPIOISRVector;
    pinIsr.m_param   = NULL;
}

//--//

void AT91_GPIO_Driver::EnableOutputPin( GPIO_PIN pin, BOOL initialState )
{
    struct AT91_PIO * pPioX;

    DBGGPIO("EnableOutputPin",pin);

    // MAKE SURE CLR DOES NOT REPROGRAM SPI CHIP SELCTS
	if (pin == AT91_SPI0_CS0 ||
	    /*pin == AT91_SPI0_CS1 ||	allow SD CARD to maintain CS*/		
		pin == AT91_SPI0_CS2 ||
		pin == AT91_SPI0_CS3 ||
		pin == AT91_SPI1_CS0 ||
	    pin == AT91_SPI1_CS1 ||
		pin == AT91_SPI1_CS2 ||
		pin == AT91_SPI1_CS3) {
		DBGGPIO("don't enable spi chipselects as in or output",pin);
		return;
	}

	ASSERT(pin < c_MaxPins);

	UINT32   bitmask  = 1 << PinToBit( pin );
    UINT32  port = PinToPort( pin );

    pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + port * 0x200);

        GLOBAL_LOCK(irq);

    pPioX->PIO_PER = bitmask;                   // Enable PIO function

	if(initialState)
        pPioX->PIO_SODR = bitmask;
    else
        pPioX->PIO_CODR = bitmask;

    pPioX->PIO_OER = bitmask;                   // Enable Output

    PIN_ISR_DESCRIPTOR& pinIsr = g_AT91_GPIO_Driver.m_PinIsr[ pin ];

    pinIsr.m_intEdge = GPIO_INT_NONE;
    pinIsr.m_isr     = STUB_GPIOISRVector;
    pinIsr.m_param   = NULL;

}

//--//

BOOL AT91_GPIO_Driver::EnableInputPin( GPIO_PIN pin, BOOL GlitchFilterEnable, GPIO_INTERRUPT_SERVICE_ROUTINE ISR, void* pinIsrParam, GPIO_INT_EDGE intEdge, GPIO_RESISTOR resistorState )
{
    struct AT91_PIO * pPioX;

	DBGGPIO("EnableInputPin",pin);

    ASSERT(pin < c_MaxPins);

	// MAKE SURE NOONE DOES NOT REPROGRAM SPI CHIP SELCTS
	if (pin == AT91_SPI0_CS0 ||
	    pin == AT91_SPI0_CS1 ||
		pin == AT91_SPI0_CS2 ||
		pin == AT91_SPI0_CS3 ||
		pin == AT91_SPI1_CS0 ||
	    pin == AT91_SPI1_CS1 ||
		pin == AT91_SPI1_CS2 ||
		pin == AT91_SPI1_CS3) {
		DBGGPIO("don't enable spi chipselects as in or output",pin);
		return TRUE;
	}

	UINT32   bitmask  = 1 << PinToBit( pin );
    UINT32  port = PinToPort( pin );

    pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + port * 0x200);

    GLOBAL_LOCK(irq);

    switch (resistorState)
    {
        case RESISTOR_DISABLED:
            pPioX->PIO_PPUDR = bitmask;     // Disable the pull up resistor
            break;
        case RESISTOR_PULLUP:
            pPioX->PIO_PPUER =  bitmask;        // Enable the pull up resistor
            break;
        case RESISTOR_PULLDOWN:
            return FALSE;                       // There are no pulldown resistors on the SAM9
    }

    pPioX->PIO_IFER = bitmask;              // Enable Glitch Filter, this feature is different from debouncing
/*
    if(GlitchFilterEnable)
        pPioX->PIO_IFER = bitmask;
    else
        pPioX->PIO_IFDR = bitmask;
*/
    PIN_ISR_DESCRIPTOR& pinIsr = g_AT91_GPIO_Driver.m_PinIsr[ pin ];

    pPioX->PIO_PER = bitmask;                   // Enable PIO function
    pPioX->PIO_ODR = bitmask;               // Disable Output

    pinIsr.m_intEdge = intEdge;
    pinIsr.m_isr     = (ISR != NULL) ? ISR : STUB_GPIOISRVector;
    pinIsr.m_param   = pinIsrParam;
    pinIsr.m_flags   = GlitchFilterEnable ? PIN_ISR_DESCRIPTOR::c_Flags_Debounce : 0;
    pinIsr.m_status  = 0;
    pinIsr.m_completion.Abort();
    pinIsr.m_completion.Initialize();

    if(ISR)
    {
        // since there is no HW debounce, we always have to listen to both edges and
        // filter the interrupt based on the desired edge and the debounce time
        switch(intEdge)
        {
            case GPIO_INT_NONE:
                return FALSE;

            case GPIO_INT_EDGE_LOW :
            case GPIO_INT_LEVEL_LOW:
                pinIsr.m_status |= PIN_ISR_DESCRIPTOR::c_Status_AllowLowEdge;
                break;

            case GPIO_INT_EDGE_HIGH:
            case GPIO_INT_LEVEL_HIGH:
                pinIsr.m_status |= PIN_ISR_DESCRIPTOR::c_Status_AllowHighEdge;
                break;

            case GPIO_INT_EDGE_BOTH:
            {
                pinIsr.m_flags |= PIN_ISR_DESCRIPTOR::c_Flags_RequireHighEdge | PIN_ISR_DESCRIPTOR::c_Flags_RequireLowEdge;

                // check pin state before setting interrupt
                while(true)
                {
                    BOOL state = pPioX->PIO_PDSR & bitmask;

                    if(state)
                    {
                        pinIsr.m_status &= ~PIN_ISR_DESCRIPTOR::c_Status_AllowHighEdge;
                        pinIsr.m_status |=  PIN_ISR_DESCRIPTOR::c_Status_AllowLowEdge;

                        // make sure we didn't miss an edge interrupt (and leave it deadlocked!)
                        if( !(pPioX->PIO_PDSR & bitmask) )
                        {
                            continue;
                        }
                    }
                    else
                    {
                        pinIsr.m_status &= ~PIN_ISR_DESCRIPTOR::c_Status_AllowLowEdge;
                        pinIsr.m_status |=  PIN_ISR_DESCRIPTOR::c_Status_AllowHighEdge;

                        // make sure we didn't miss an edge interrupt (and leave it deadlocked!)
                        if( pPioX->PIO_PDSR & bitmask )
                        {
                            continue;
                        }
                    }
                    break;
                }
            }
            break;

        default:
            ASSERT(0);
            return FALSE;
        }

        pPioX->PIO_IER = bitmask;               // Enable interrupt
    }
    else
    {
        pPioX->PIO_IDR = bitmask;               // Disable interrupt
    }

    return TRUE;
}


BOOL AT91_GPIO_Driver::GetPinState( GPIO_PIN pin )
{
    struct AT91_PIO * pPioX;

	//DBGGPIO("GetPinState",pin);

    ASSERT(pin < c_MaxPins);

    UINT32   bit  = PinToBit( pin );
    UINT32  port = PinToPort( pin );

    pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + port * 0x200);

        return (((pPioX->PIO_PDSR) >> bit) & 1);
}

//--//

void AT91_GPIO_Driver::SetPinState( GPIO_PIN pin, BOOL pinState )
{
    struct AT91_PIO * pPioX;

	//DBGGPIO("SetPinState",pin);

    ASSERT(pin < c_MaxPins);

    UINT32  port = PinToPort( pin );
    UINT32  bit  = PinToBit ( pin );

    pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + port * 0x200);


    GLOBAL_LOCK(irq);

    if(pinState)
        pPioX->PIO_SODR =  1 << bit;
    else
        pPioX->PIO_CODR  = 1 << bit;
}

//--//

// 16:21 16.10.2009: Rewritten, Volker Goller, mcoom software GmbH & Co.
void AT91_GPIO_Driver::ISR( void* Param )
{
    struct AT91_PIO * pPioX;

    UINT32  port = (UINT32)Param;
	int fire;

    pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + port * 0x200);


    // fire off each change, one at a time
    UINT32 bitMask  = 0x00000001;
    UINT32 bitIndex = 0;
	
	// We need to mask ISR with IMR or we will get false trues!	
	// IMR is the interrupt mask register ( bit set enables interrupt )
	// ISR each bit set signals a changed input bit
	// please note that ISR reports changes even if the associated interrupt is disabled
	// example: on port C we have the 16-Bit Databus in in upper 16-Bits. Bit changes on the databus 
	// are reported in ISR if they survive the noise filter! I have seen it.
    UINT32 interruptsActive = (pPioX->PIO_ISR & pPioX->PIO_IMR);
	
	// need to capture data as close to isr status as possible
	// mask with IMR, too
	UINT32 capture = (pPioX->PIO_PDSR & pPioX->PIO_IMR);
	
	// use a seperate variable here for debugging purposes
	UINT32 change  = interruptsActive;	
	
	// ok, get the rising edges
	UINT32 rising  = change & capture;
	
	// and finally the falling edges
	UINT32 falling = change & ~capture;

	// scan thru all pins
    while(interruptsActive && change)
    {
		// find next higher set
        while((interruptsActive & bitMask) == 0)
        {
            bitMask  <<= 1;
            ++bitIndex;
        }
		
		// reset that bit so that when all handles, 
		// the while loop will be terminated 
		interruptsActive &= ~bitMask;
		
		// presume we will fire an interrupt
		fire=TRUE;

        //--//
		
		GPIO_PIN pin = BitToPin( bitIndex, port);
		
        PIN_ISR_DESCRIPTOR& pinIsr = g_AT91_GPIO_Driver.m_PinIsr[ pin ];

		// so, what moe are we in?
        switch (pinIsr.m_intEdge)
		{
			case GPIO_INT_NONE:
				// should never happen.
				fire = FALSE;
				break;
				
			case GPIO_INT_EDGE_BOTH:
				// have we changed at all, fire
				fire = (change & bitMask) != 0;
				break;
				
			case GPIO_INT_EDGE_LOW :
				// falling edge detected? Fire ..
				fire = (falling & bitMask) != 0;
                break;

			case GPIO_INT_LEVEL_LOW:
				// is pin low now? Fire ..
                fire = (capture & bitMask) == 0;
                break;

            case GPIO_INT_LEVEL_HIGH:
				// is pin high now? Fire ..
				fire = (capture & bitMask) != 0;
                break;

			case GPIO_INT_EDGE_HIGH:
                // rising edge detected? Fire ..
				fire = (rising & bitMask) != 0;
                break;
			
			default:
				// no way to get here ...
				fire = FALSE;
		}			

		if (fire) 
		{
			// debounce does not work at all, so forget it for now!
			pinIsr.m_isr( pin, (capture & bitMask) >> bitIndex , pinIsr.m_param );  // send low = FALSE, high = TRUE, plus preset parameter
		}
    }
}
//--//

void AT91_GPIO_Driver::SetResistor( GPIO_PIN pin, GPIO_RESISTOR resistorState )
{
    struct AT91_PIO * pPioX;

	DBGGPIO("SetResistor",pin);

    ASSERT(pin < c_MaxPins);

    UINT32   bitmask  = 1 << PinToBit( pin );
    UINT32  port = PinToPort( pin );

    pPioX = (struct AT91_PIO *)(AT91C_BASE_PIOA + port * 0x200);

    GLOBAL_LOCK(irq);

    switch (resistorState)
    {
        case RESISTOR_DISABLED:
            pPioX->PIO_PPUDR = bitmask;     // Disable the pull up resistor
            break;
        case RESISTOR_PULLUP:
            pPioX->PIO_PPUER =  bitmask;        // Enable the pull up resistor
            break;
        default:
            ASSERT(0);                          // There are no pulldown resistors on the SAM9
    }
    return;
}

//--//

BOOL AT91_GPIO_Driver::PinIsBusy( GPIO_PIN pin )
{
	DBGGPIO("PinIsBusy",pin);

    ASSERT(pin < c_MaxPins);

    UINT32  port     = pin / c_PinsPerPort;
    UINT32  bitMask  = 1 << PinToBit( pin );
    UINT32& res      = g_AT91_GPIO_Driver.m_PinReservationInfo[port];

    return (res & bitMask) != 0 ? TRUE : FALSE;
}

//--//

BOOL AT91_GPIO_Driver::ReservePin( GPIO_PIN pin, BOOL fReserve )
{
    GLOBAL_LOCK(irq);

	DBGGPIO("ReservePin",pin);

    ASSERT(pin < c_MaxPins);

    UINT32  port = pin / c_PinsPerPort;
    UINT32  bit  = PinToBit( pin );
    UINT32& res  = g_AT91_GPIO_Driver.m_PinReservationInfo[port];
    UINT32  mask = (fReserve ? 1u : 0u) << bit;

    if((res & (1u << bit)) == mask) return FALSE;

    if(fReserve)
    {
        res |= (1u << bit);
    }
    else
    {
        res &= ~(1u << bit);
    }

    return TRUE;
}

//--//

UINT32 AT91_GPIO_Driver::GetDebounce()
{
    return CPU_TicksToTime( g_AT91_GPIO_Driver.m_DebounceTicks ) / 10000;
}

BOOL AT91_GPIO_Driver::SetDebounce( INT64 debounceTimeMilliseconds )
{
    if(debounceTimeMilliseconds < c_MinDebounceTimeMs || c_MaxDebounceTimeMs < debounceTimeMilliseconds )
    {
        return FALSE;
    }

    g_AT91_GPIO_Driver.m_DebounceTicks = CPU_MillisecondsToTicks( (UINT32)debounceTimeMilliseconds );

    return TRUE;
}

void AT91_GPIO_Driver::GetPinsMap( UINT8* pins, size_t size )
{
    const UINT8*    src = c_Gpio_Attributes;
    UINT8* dst = pins;
    UINT32 maxpin = c_MaxPins;

    if ( size ==0 ) return;
    while ((size--) && (maxpin --))
    {
        *dst = *src;
        ++dst; ++src;
    }
}

//--//

void AT91_GPIO_Driver::STUB_GPIOISRVector( GPIO_PIN Pin, BOOL PinState, void* Param )
{
    lcd_printf( "\fSTUB_GPI %04x\r\n", (size_t)Param );

    DEBUG_HARD_BREAKPOINT();
}

//--//

void AT91_GPIO_Driver::PIN_ISR_DESCRIPTOR::Fire( void* arg )
{
    PIN_ISR_DESCRIPTOR* desc = (PIN_ISR_DESCRIPTOR*)arg;

    desc->m_isr( desc->m_pin, (desc->m_status & c_Status_AllowHighEdge) != 0, desc->m_param );


    if(desc->m_intEdge == GPIO_INT_EDGE_BOTH)
    {
        desc->m_status ^= PIN_ISR_DESCRIPTOR::c_Status_AllowLowEdge | PIN_ISR_DESCRIPTOR::c_Status_AllowHighEdge;
    }
}

//--//

void AT91_GPIO_Driver::PIN_ISR_DESCRIPTOR::HandleDebounce( BOOL edge )
{
    ASSERT_IRQ_MUST_BE_OFF();

    m_completion.Abort();

    UINT8 statusMask = edge ? c_Status_AllowHighEdge  : c_Status_AllowLowEdge ;

    if(m_status & statusMask)
    {
        m_completion.EnqueueTicks( Time_CurrentTicks() + g_AT91_GPIO_Driver.m_DebounceTicks );
    }
}

